﻿// // ******************************************************************
// //       /\ /|       @file       VideoFileUtils.cs
// //       \ V/        @brief      await29
// //       | "")       @author     Await, 285283010@qq.com
// //       /  |
// //      /  \\        @Modified   2025-02-21 20:02
// //    *(__\_\        @Copyright  Copyright (c) , 2025-02-04 14:02 Await
// // ******************************************************************

using System.Collections.Concurrent;
using Tesseract;
using Xabe.FFmpeg;
using Xabe.FFmpeg.Events;
using Xabe.FFmpeg.Streams.SubtitleStream;

namespace AutomaticSubtitles.Service.Infrastructure.Helper;

public static class VideoFileUtils
{
    private static Dictionary<string, string> _wantedExtMap = new();
    private static Dictionary<string, string> _defExtMap = new();

    // 判断是否是 BDMV 蓝光目录
    public static (bool, string) FileNameIsBDMV(string idBdmvFilePath)
    {
        if (!Path.GetFileName(idBdmvFilePath).Equals(Common.FileBDMV, StringComparison.CurrentCultureIgnoreCase)) return (false, "");
        var cerDir = Path.GetDirectoryName(idBdmvFilePath);
        var directoryName = Path.GetDirectoryName(cerDir);
        // directoryName = Path.GetDirectoryName(directoryName);
        var bdmvDir = Path.Combine(directoryName, "BDMV");
        if (IsDir(cerDir) && IsDir(bdmvDir))
            return (true, Path.Combine(directoryName, Path.GetFileNameWithoutExtension(cerDir) + Common.VideoExtMp4));

        return (false, "");
    }

    // 后缀名是否符合规则
    public static bool IsWantedVideoExtDef(string fileName)
    {
        if (_wantedExtMap.Count < 1)
        {
            _defExtMap[Common.VideoExtMp4] = Common.VideoExtMp4;
            _defExtMap[Common.VideoExtMkv] = Common.VideoExtMkv;
            _defExtMap[Common.VideoExtRmvb] = Common.VideoExtRmvb;
            _defExtMap[Common.VideoExtIso] = Common.VideoExtIso;
            _defExtMap[Common.VideoExtM2ts] = Common.VideoExtM2ts;

            _wantedExtMap[Common.VideoExtMp4] = Common.VideoExtMp4;
            _wantedExtMap[Common.VideoExtMkv] = Common.VideoExtMkv;
            _wantedExtMap[Common.VideoExtRmvb] = Common.VideoExtRmvb;
            _wantedExtMap[Common.VideoExtIso] = Common.VideoExtIso;
            _wantedExtMap[Common.VideoExtM2ts] = Common.VideoExtM2ts;

            // foreach (var videoExt in settings.Get().AdvancedSettings.CustomVideoExts) _wantedExtMap[videoExt] = videoExt;
        }

        var fileExt = Path.GetExtension(fileName).ToLower();
        return _wantedExtMap.ContainsKey(fileExt);
    }

    // 判断路径是否是目录
    private static bool IsDir(string path)
    {
        return Directory.Exists(path);
    }

    /// <summary>
    /// 判断是否是软连接的问题
    /// </summary>
    /// <param name="curFile"></param>
    /// <param name="fileFullPath"></param>
    /// <returns></returns>
    public static bool IsSoftConnection(FileInfo curFile, string fileFullPath)
    {
        if (curFile.Attributes.HasFlag(FileAttributes.Directory))
            if (curFile.Name.StartsWith(".@__thumb"))
                return true;

        // 跳过不符合的文件，比如 MAC OS 下可能有缓存文件，见 #138
        // 尝试获取文件信息
        try
        {
            // 封面缓存文件夹中的文件都要跳过 .@__thumb  #581
            // 获取这个文件的父级文件夹的名称，然后判断是否是 .@__thumb 开头的
            var parentFolderName = Path.GetFileName(Path.GetDirectoryName(fileFullPath));
            if (parentFolderName.StartsWith(".@__thumb")) return true;

            // 软链接问题 #558
            if (curFile.Length < 1000 && !curFile.Attributes.HasFlag(FileAttributes.ReparsePoint)) // 确认是软连接
                return true;
            if (curFile.Length == 4096 && curFile.Name.StartsWith("._")) return true;
            if (curFile.Name.Replace(Path.GetExtension(curFile.Name), "").EndsWith("-trailer")) return true;
        }
        catch (Exception ex)
        {
            return true;
        }

        return false;
    }

    public static bool IsSubExtWanted(string ext)
    {
        return Common.WantedExts.Any(wantedExt => string.Equals(ext, wantedExt, StringComparison.OrdinalIgnoreCase));
    }

    /// <summary>
    /// 判断字幕语言类型
    /// </summary>
    /// <param name="curFile"></param>
    /// <returns></returns>
    public static string? GetLanguage(string curFile)
    {
        try
        {
            //催眠 (2023) - 1080p.zh.srt
            var fileName = Path.GetFileNameWithoutExtension(curFile);
            var index = fileName.LastIndexOf(".", StringComparison.Ordinal);
            if (index == -1) return null;
            var language = fileName.Substring(index + 1);
            return language;
        }
        catch (Exception e)
        {
            return null;
        }
    }

    /// <summary>
    /// 读取媒体文件中的字幕文件
    /// </summary>
    /// <param name="path">地址</param>
    /// <param name="isMove">是否电影</param>
    /// <param name="wantedExt">需要的类型</param>
    /// <param name="isDefalut">是否获取默认字幕</param>
    /// <returns></returns>
    // /// <param name="isAddSub">是否强制提取</param>
    public static async Task<Dictionary<string, List<(int, string)>>?> ReadSubtitlesMediaFiles(string path, bool isMove, Dictionary<string, bool> wantedExt, bool isDefalut = false)
    {
        // 确保FFmpeg已下载
        // await FFmpegDownloader.GetLatestVersion(FFmpegVersion.Official);
        using var source = new CancellationTokenSource();
        var mediaInfo = await ObtainMediaInformation(path, source);
        if (mediaInfo == null) return null;
        try
        {
            var subtitleStream = mediaInfo.SubtitleStreams.FirstOrDefault();
            if (subtitleStream == null || mediaInfo.SubtitleStreams == null)
            {
                Console.WriteLine($@"视频文件中找不到字幕流. {path}");
                return null;
            }

            Dictionary<string, List<(int, string)>>? data = null;
            if (mediaInfo.SubtitleStreams == null) return data;
            if (isDefalut)
            {
                //因为没有找到想要的字幕 此处获取默认的第一个字幕
                data = new Dictionary<string, List<(int, string)>>();
                foreach (var sitem in mediaInfo.SubtitleStreams)
                {
                    //剔除sitem.Path带有双重"的问题
                    var cPath = sitem.Path;
                    if (cPath.Contains('"'))
                        cPath = cPath.Replace("\"", "");

                    if (data.TryGetValue(sitem.Language, out var value))
                        value.Add((sitem.Index, cPath));
                    else
                        data.TryAdd(sitem.Language, [(sitem.Index, cPath)]);
                }
                // var firstOrDefault = mediaInfo.SubtitleStreams!.FirstOrDefault(x => x.Language == Common.NoSubDefaultKey);
                // if (firstOrDefault == null)
                // {
                //     var firstOrDefault2 = mediaInfo.SubtitleStreams.First();
                //     data.TryAdd(firstOrDefault2.Language, [firstOrDefault2.Path]);
                // }
                // else
                // {
                //     data.TryAdd(Common.NoSubDefaultKey, [firstOrDefault.Path]);
                // }

                return data;
            }


            var globalPath = VideoListHelper.GetCachePath(isMove);
            Console.WriteLine($@"长度:{mediaInfo.SubtitleStreams?.Count() ?? 0} 字幕流.{path}");
            if (!Directory.Exists(globalPath)) Directory.CreateDirectory(globalPath);
            var outputFilePath = $"{globalPath}/{Path.GetFileName(path).Replace(Path.GetExtension(path), "")}";
            foreach (var sitem in mediaInfo.SubtitleStreams!)
            {
                //默认全部字幕记录，
                if (sitem == null) continue;
                //剔除sitem.Path带有双重"的问题
                var cPath = sitem.Path;
                if (cPath.Contains('"'))
                    cPath = cPath.Replace("\"", "");
                data ??= new Dictionary<string, List<(int, string)>>();
                if (data.TryGetValue(sitem.Language, out var value))
                    value.Add((sitem.Index, cPath));
                else
                    data.TryAdd(sitem.Language, [(sitem.Index, cPath)]);
            }

            if (data == null) return data;
            var srtList = new ConcurrentDictionary<string, List<string>>();
            var enumerable = wantedExt.Where(x => x.Value).Select(x => x.Key);
            var pathList = new List<(int, string)>();
            foreach (var se in enumerable)
            {
                data.TryGetValue(se, out var subtitleStreams);
                if (subtitleStreams == null) continue;
                //如有有ass文件，则优先提取ass文件
                (int, string)? assItem = subtitleStreams.FirstOrDefault(x => x.Item2.EndsWith(".ass", StringComparison.OrdinalIgnoreCase));
                if (assItem != null)
                {
                    pathList.Add(assItem.Value);
                    continue;
                }

                pathList.Add(subtitleStreams.First());
            }

            if (pathList.Count == 0) return data;

            var tasks = pathList.Select(s => Task.Run(async () =>
            {
                try
                {
                    var citem = mediaInfo.SubtitleStreams.FirstOrDefault(x => x.Path == s.Item2 && x.Index == s.Item1);
                    var obtainMediaSubtitles = await ObtainMediaSubtitles(citem, outputFilePath, path, source);
                    if (obtainMediaSubtitles != null)
                    {
                        var tryGetValue = srtList.TryGetValue(obtainMediaSubtitles.Value.Item1, out var value);
                        if (tryGetValue)
                        {
                            value ??= [];
                            value.Add(obtainMediaSubtitles.Value.Item2);
                        }
                        else
                        {
                            srtList.TryAdd(obtainMediaSubtitles.Value.Item1, [obtainMediaSubtitles.Value.Item2]);
                        }

                        Console.WriteLine(@"缓存文件存在结束字幕流提取");
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine($@"字幕提取失败1 :{e.Message}_{path}");
                }
            }, source.Token));
            await Parallel.ForEachAsync(tasks, new ParallelOptions
            {
                MaxDegreeOfParallelism = 2
            }, async (task, _) => await task);
            foreach (var (key, value) in srtList)
                if (data.TryGetValue(key, out var srtList1))
                    srtList1.AddRange(value.Select(x => (-1, x)));
                else
                    data.TryAdd(key, value.Select(x => (-1, x)).ToList());

            // //TODO:需要修改为删选需要的类型，并且添加是否轻质提取的操作
            // foreach (var se in wantedExt)
            // {
            //     var subtitleStreams = mediaInfo.SubtitleStreams!.Where(x => x.Language == se.Key)?.ToList();
            //     if (subtitleStreams == null) continue;
            //
            //     if (!se.Value)
            //     {
            //         data ??= new Dictionary<string, List<string>>();
            //         data.TryAdd(se.Key, subtitleStreams.Select(x => x.Title ?? x.Path).ToList());
            //         continue;
            //     }
            //
            //     var srtList = new ConcurrentBag<string>();
            //
            //     var tasks = subtitleStreams.Select(citem => Task.Run(async () =>
            //     {
            //         try
            //         {
            //             var filePath = $"{outputFilePath}.{citem.Language}{srt}";
            //             Console.WriteLine($@"开始字幕流{item}提取");
            //
            //             if (File.Exists(filePath) && new FileInfo(filePath).Length != 0)
            //             {
            //                 srtList.Add(filePath);
            //                 Console.WriteLine(@"缓存文件存在结束字幕流提取");
            //                 return;
            //             }
            //
            //             var conversion = FFmpeg.Conversions.New()
            //                 .AddParameter($"-i \"{item}\"")
            //                 .AddParameter($"-map 0:{citem.Index} \"{filePath}\"");
            //             await conversion.Start();
            //             Console.WriteLine($@"结束字幕流{item}提取");
            //             srtList.Add(filePath);
            //         }
            //         catch (Exception e)
            //         {
            //             Console.WriteLine($@"字幕提取失败1 :{e.Message}_{item}");
            //         }
            //     }));
            //     await Parallel.ForEachAsync(tasks, async (task, _) => await task);
            //     // await Task.WhenAll(tasks);
            //     data ??= new Dictionary<string, List<string>>();
            //     data.TryAdd(se.Key, srtList.ToList());
            // }

            return data;
        }
        catch (Exception e)
        {
            Console.WriteLine($@"字幕提取失败2 :{e.Message}_{path}");
        }


        return null;
    }

    /// <summary>
    /// 获取媒体字幕
    /// </summary>
    /// <param name="subItem">字幕信息</param>
    /// <param name="outputFilePath">输出地址</param>
    /// <param name="item">地址</param>
    /// <param name="source"></param>
    /// <returns></returns>
    public static async Task<(string, string)?> ObtainMediaSubtitles(ISubtitleStream? subItem, string outputFilePath, string item, CancellationTokenSource source)
    {
        string? filePath = null;
        try
        {
            // var citem = mediaInfo.SubtitleStreams.FirstOrDefault(x => x.Path == s.Item2 && x.Index == s.Item1);
            if (subItem.Codec == SubtitleCodec.hdmv_pgs_subtitle.ToString()) //PGS字幕提取
                try
                {
                    Console.WriteLine($@"开始PGS字幕提取:" + item);
                    var convertToSrtAsync = await SubtitleConversionHelper.ConvertToSrtAsync(subItem, outputFilePath);
                    Console.WriteLine($@"结束PGS字幕提取:{item}");

                    return (subItem.Language, convertToSrtAsync);
                }
                catch (Exception e)
                {
                    Console.WriteLine($@"PGS字幕提取失败:{e.Message}_{item}");
                    return null;
                }

            //
            // //保存临时文件sup文件
            // var tempFilePath = $"{outputFilePath}.{subItem.Language}.{subItem.Index}.sup";
            // if (File.Exists(tempFilePath))
            // {
            //     if (new FileInfo(tempFilePath).Length <= 1)
            //     {
            //         if (File.Exists(tempFilePath)) File.Delete(tempFilePath);
            //     }
            //     else
            //     {
            //         Console.WriteLine(@"缓存文件存在结束字幕流提取");
            //     }
            // }
            // else
            // {
            //     var conversion = FFmpeg.Conversions.New()
            //         .AddParameter($"-i \"{item}\"")
            //         .AddParameter($"-map 0:{subItem.Index} -c copy \"{tempFilePath}\"");
            //     conversion.OnVideoDataReceived += VideoDataEventHandler;
            //     await conversion.Start(source.Token);
            // }
            //
            // // 转换为srt文件
            // filePath = $"{outputFilePath}.{subItem.Language}.{subItem.Index}{Common.DefaultSrt}";
            // Console.WriteLine($@"开始转换为srt文件：" + filePath);
            // await TesseractHelper.ConvertSupToSrt(subItem.Language, tempFilePath, filePath);
            // Console.WriteLine($@"结束转换为srt文件：" + filePath);
            // return (subItem.Language, filePath);
            if (subItem != null)
            {
                Console.WriteLine($@"开始提取字幕流:{item}");
                filePath = $"{outputFilePath}.{subItem.Language}.{subItem.Index}{Common.DefaultSrt}";
                if (File.Exists(filePath))
                {
                    if (new FileInfo(filePath).Length <= 1)
                    {
                        if (File.Exists(filePath)) File.Delete(filePath);
                    }
                    else
                    {
                        Console.WriteLine(@"缓存文件存在结束字幕流提取");
                        return (subItem.Language, filePath);
                    }
                }

                var conversion = FFmpeg.Conversions.New()
                    .AddParameter($"-i \"{item}\"")
                    .AddParameter($"-map 0:{subItem.Index} \"{filePath}\"");
                conversion.OnVideoDataReceived += VideoDataEventHandler;
                await conversion.Start(source.Token);
                Console.WriteLine($@"结束提取字幕流:{item}");
                return (subItem.Language, filePath);
            }
        }
        catch (Exception e)
        {
            Console.WriteLine($@"字幕提取失败1 :{e.Message}_{item}");
            // 删除文件
            if (File.Exists(filePath)) File.Delete(filePath);
        }

        return null;
    }

    public static void VideoDataEventHandler(object sender, VideoDataEventArgs args)
    {
        Console.WriteLine(args.Data);
        Console.WriteLine("ssss" + sender);
    }

    /// <summary>
    /// 获取媒体数据
    /// </summary>
    /// <param name="path"></param>
    /// <param name="source"></param>
    /// <returns></returns>
    public static async Task<IMediaInfo?> ObtainMediaInformation(string path, CancellationTokenSource source)
    {
        var attempts = 0;
        IMediaInfo? mediaInfo = null;
        while (attempts < 3)
            try
            {
                // 执行操作
                mediaInfo = await FFmpeg.GetMediaInfo(path, source.Token);
                break;
            }
            catch (Exception e)
            {
                var msg = $"读取媒体文件失败,尝试重新读取: {path}" + e.Message;
                Console.WriteLine(msg);
                attempts++;
            }

        return mediaInfo;
    }

    /// <summary>
    /// 媒体的信息
    /// </summary>
    /// <param name="path"></param>
    /// <returns></returns>
    public static Nfo? NfoMoveInformationOfTheMedia(string? path)
    {
        if (string.IsNullOrWhiteSpace(path)) return null;
        //判断传入的路径是不是nfo文件，如果不是获取同路径同名下是否存在nfo文件
        if (path.EndsWith(".nfo", StringComparison.OrdinalIgnoreCase)) return NfoTvInformation(path);
        var nfoPath = Path.Combine(Path.GetDirectoryName(path)!, Path.GetFileNameWithoutExtension(path) + ".nfo");
        if (File.Exists(nfoPath))
        {
            path = nfoPath;
        }
        else
        {
            var combine = Path.Combine(Path.GetDirectoryName(path)!, "movie.nfo");
            if (File.Exists(combine))
                path = nfoPath;
        }

        return NfoTvInformation(path);
    }

    /// <summary>
    /// 媒体的信息
    /// </summary>
    /// <param name="path"></param>
    /// <returns></returns>
    public static Nfo? NfoTvInformationOfTheMedia(string? path)
    {
        if (string.IsNullOrWhiteSpace(path)) return null;
        //判断传入的路径是不是nfo文件，如果不是获取同路径同名下是否存在nfo文件
        if (path.EndsWith(".nfo", StringComparison.OrdinalIgnoreCase)) return NfoTvInformation(path);
        var nfoPath = Path.Combine(Path.GetDirectoryName(path)!, Path.GetFileNameWithoutExtension(path) + ".nfo");

        if (File.Exists(nfoPath))
        {
            path = nfoPath;
        }
        else
        {
            var combine = Path.Combine(Path.GetDirectoryName(path)!, "tvshow.nfo");
            if (File.Exists(combine))
            {
                path = nfoPath;
            }
            else
            {
                var combine1 = Path.Combine(Path.GetDirectoryName(path)!, "season.nfo");
                if (File.Exists(combine1)) path = nfoPath;
            }
        }

        return NfoTvInformation(path);
    }

    /// <summary>
    /// 获取基本nfo信息
    /// </summary>
    /// <param name="path"></param>
    /// <returns></returns>
    private static Nfo? NfoTvInformation(string? path)
    {
        //判断文件是否存在
        if (string.IsNullOrWhiteSpace(path) || !File.Exists(path)) return null;
        //读取文件内容
        var content = File.ReadAllText(path);
        //解析文件内容
        var nfo = NfoParserHelper.Parse(content);
        return nfo.Item1;
    }
}